using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Xml;
using System.Net;

using StormModelToKMLDB;
using EnterpriseDT.Net.Ftp;
using KmlUtils;

// For Zip
using java.util.zip;
using java.io;

namespace TropicalWatchUI
{
    public partial class Form1 : Form
    {
        private bool _newSST = true;
        private class OverlayKey : LatLonBox
        {
            string _imgType;

            public OverlayKey(LatLonBox llb, string imgType)
                : base(llb.North, llb.South, llb.East, llb.West, llb.Rotation)
            {
                this._imgType = imgType;
            }

            public string ImgType
            {
                get { return _imgType; }
                set { _imgType = value; }
            }

            public string FileName
            {
                get
                {
                    return "_" +_imgType + "_" + this.South.ToString() + "_" + this.West.ToString() + ".bmp";
                }
            }

            public string KmlName
            {
                get
                {
                    return "(" + this.South + "," + this.West + ")->(" + this.North + "," + this.East + ")";
                }
            }
        }
        private delegate void TextBoxDelegate(string txt);
        private Thread _uploadThread;
        Database _db;
        ModelGetter _mg;
        SSTGetter _sg;
        NHCGetter _ng;
        Dictionary<string, OverlayKey> _sstNames = new Dictionary<string, OverlayKey>();
        List<Buoy> _uploadBuoys = new List<Buoy>();
        private int _dbCleanupInterval = 10; // mins
        private int _modelUpdateInterval = 10; //mins
        private int _sstUpdateInterval = 60; //mins
        private int _kmlUploadInterval = 10; // mins
        private int _maxRadarDistFromStorm = 350;//NM
        private int _maxBuoyDistFromStorm = 350;//NM
        private object _distanceLock = new object();

        public Form1()
        {
            InitializeComponent();

            //// Create a new DB Fil...we don't need long persistence
            //if (System.IO.File.Exists("tropicalWatch.yap"))
            //    System.IO.File.Delete("TropicalWatch.yap");

            _db = new Database("tropicalWatch.yap", this._dbCleanupInterval);
            _db.LogMessage += new LogDelegate(_db_LogMessage);

            _mg = new ModelGetter(this._modelUpdateInterval);
            _mg.ModelsRetrieved += new ModelGetter.ModelsRetrievedHandler(_mg_ModelsRetrieved);

            _sg = new SSTGetter(this._sstUpdateInterval);
            _sg.CurrentSSTRetrieved += new SSTGetter.CurrentSSTHandler(_sg_CurrentSSTRetrieved);
            _sg.CurrentSST3DeltaRetrieved += new SSTGetter.CurrentSST3DeltaHandler(_sg_CurrentSST3DeltaRetrieved);
            _sg.CurrentSST7DeltaRetrieved += new SSTGetter.CurrentSST7DeltaHandler(_sg_CurrentSST7DeltaRetrieved);
            _sg.LogMessage += new LogDelegate(_sg_LogMessage);

            _ng = new NHCGetter(this._modelUpdateInterval);
            _ng.DiscussionRetrieved += new NHCGetter.DiscussionRetrievedHandler(_ng_DiscussionRetrieved);

            _uploadThread = new Thread(new ThreadStart(this.UploadKml));
            _uploadThread.IsBackground = true;
            _uploadThread.Name = "KmlUploadThread";

            // Check the box to encourage users to enter proper information in ftp boxes
            this.checkBox1.Checked = true;

            this._maxBuoyDistFromStorm = (int)buoyDist.Value;
            this._maxRadarDistFromStorm = (int)radarDist.Value;
        }

        void _ng_DiscussionRetrieved(List<NHCDiscussion> models)
        {
            foreach (NHCDiscussion n in models)
            {
                _db.AddDiscussionToStorm(n);
            }
        }

        void _sg_LogMessage(string txt)
        {
            this.AppendLog(txt);
        }

        void _sg_CurrentSST7DeltaRetrieved(TempBitmap bm)
        {
            OverlayKey seven = new OverlayKey(bm.LatLonBox, "7Delta");

            // Now make the proper bitmaps from these TempMaps
            AppendLog(bm.Filename + " (" + bm.Lat + "," + bm.Lon + ") retrieved");

            // Get the Current TempMap so we can difference the 2
            TempBitmap curr = _db.GetCurrentSST(bm.Lat, bm.Lon, bm.SpanDeg);

            if (curr == null)
                AppendLog("Current bitmap for (" + bm.Lat + "," + bm.Lon + ") could not be retrieved from DB !!!");
            else
            {
                // Make 7 day delta SST Map
                AppendLog("Creating 7 Day Delta SST for (" + bm.Lat + "," + bm.Lon + ")...");
                TempBitmap.MakeDeltaSSTMap(curr, bm).Save(seven.FileName);
            }

            lock (this._sstNames)
            {
                this._sstNames[seven.FileName] = seven;
            }
        }

        void _sg_CurrentSST3DeltaRetrieved(TempBitmap bm)
        {
            OverlayKey three = new OverlayKey(bm.LatLonBox, "3Delta");

            // Now make the proper bitmaps from these TempMaps
            AppendLog(bm.Filename + " (" + bm.Lat + "," + bm.Lon + ") retrieved");

            // Get the Current TempMap so we can difference the 2
            TempBitmap curr = _db.GetCurrentSST(bm.Lat, bm.Lon, bm.SpanDeg);

            if (curr == null)
                AppendLog("Current bitmap for (" + bm.Lat + "," + bm.Lon + ") could not be retrieved from DB !!!");
            else
            {
                // Make 3 day delta SST Map
                AppendLog("Creating 3 Day Delta SST for (" + bm.Lat + "," + bm.Lon + ")...");
                TempBitmap.MakeDeltaSSTMap(curr, bm).Save(three.FileName);
            }

            lock(this._sstNames)
            {
                this._sstNames[three.FileName] = three;
            }
        }

        void _sg_CurrentSSTRetrieved(TempBitmap bm)
        {
            OverlayKey smooth = new OverlayKey(bm.LatLonBox, "smooth");
            OverlayKey contoured = new OverlayKey(bm.LatLonBox, "contoured");
            OverlayKey twentySixPlus = new OverlayKey(bm.LatLonBox, "twentySixPlus");
            
            // Now make the proper bitmaps from these TempMaps
            AppendLog(bm.Filename + " (" + bm.Lat + "," + bm.Lon + ") retrieved");

            // Insert into DB...it may be newer
            this._newSST = _db.InsertSST(bm);

            // Make Smooth Current SST
            AppendLog("Creating Smooth SST for (" + bm.Lat + "," + bm.Lon + ")...");
            bm.MakeSSTMap().Save(smooth.FileName);

            // Make Contoured Current SST
            AppendLog("Creating Contoured SST for (" + bm.Lat + "," + bm.Lon + ")...");
            bm.MakeContourSSTMap().Save(contoured.FileName);

            // Make 26+ Current SST
            AppendLog("Creating 26+ SST for (" + bm.Lat + "," + bm.Lon + ")...");
            bm.Make26SSTMap().Save(twentySixPlus.FileName);

            lock (this._sstNames)
            {
                this._sstNames[smooth.FileName] = smooth;
                this._sstNames[contoured.FileName] = contoured;
                this._sstNames[twentySixPlus.FileName] = twentySixPlus;
            }
        }

        void _mg_ModelsRetrieved(List<Storm> models)
        {
            foreach (Storm s in models)
            {
                _db.InsertStormModel(s);
            }
        }

        void _db_LogMessage(string txt)
        {
            this.AppendLog(txt);
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            this.EnableFtpBoxes(this.checkBox1.Checked);
        }

        private void EnableFtpBoxes(bool enable)
        {
            this.FtpLoc.Enabled = enable;
            this.FtpPass.Enabled = enable;
            this.FtpUser.Enabled = enable;
            this.HttpBox.Enabled = enable;
        }

        private void AppendLog(string txt)
        {
            // Be wary of Thread Safety
            try
            {
                if (this.textBox1.InvokeRequired)
                {
                    this.Invoke(new TextBoxDelegate(this.AppendLog), new object[] { txt });
                }
                else
                {
                    lock (this.textBox1)
                    {
                        this.textBox1.AppendText(DateTime.Now.ToString() + "> " + txt + "\n");
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("AppendLog caught Exception :" + e.Message);
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                this._mg.Stop();
                this._sg.Stop();
                this._ng.Stop();
                this._uploadThread.Interrupt();
                this._db.Close();
            }
            catch (Exception)
            {
                if(!this.Disposing)
                    this.Dispose(true);
            }
        }

        private bool FTPLoginIsValid()
        {
            bool ret = true;

            if (this.FtpLoc.Text.Length <= 0)
                ret = false;
            else if (this.FtpPass.Text.Length <= 0)
                ret = false;
            else if (this.FtpUser.Text.Length <= 0)
                ret = false;

            return ret;
        }

        private void UploadKml()
        {
            while (true)
            {
                // Check that the DB is readable
                while (!_db.Ready)
                {
                    AppendLog("Waiting for DB to load...");
                    Thread.Sleep(500);
                }

                List<Storm> currStorms = this._db.GetCurrentStorms(new TimeSpan(12, 0, 0));
                Dictionary<string, Buoy> allBuoys = this.GetAllBuoys();
                //List<Buoy> allBuoys = this.GetAllBuoys();
                List<Radar> allRadars = this.GetAllRadars();
                this._uploadBuoys.Clear();
                try
                {
                    AppendLog(string.Format("Kml: Writing {0} storms... ", currStorms.Count));
                    #region Write Kml File
                    FileStream fs = new FileStream("tropModels.kml", FileMode.Create, FileAccess.Write, FileShare.ReadWrite, 256);
                    XmlTextWriter tw = new XmlTextWriter(fs, Encoding.UTF8);
                    tw.Formatting = Formatting.Indented;
                    tw.WriteStartDocument();
                    tw.WriteStartElement("kml", "http://earth.google.com/kml/2.1");
                    tw.WriteStartElement("Document");

                    #region Styles
                    Style nhcDisc = this.MakeNHCStyle();
                    nhcDisc.WriteTo(tw);

                    Style bamm = this.MakeBAMMStyle();
                    bamm.WriteTo(tw);

                    Style bamd = this.MakeBAMDStyle();
                    bamd.WriteTo(tw);

                    Style a98e = this.MakeA98EStyle();
                    a98e.WriteTo(tw);

                    Style lbar = this.MakeLBARStyle();
                    lbar.WriteTo(tw);

                    Style initial = this.MakeInitialStyle();
                    initial.WriteTo(tw);

                    Style hist = this.MakeTrackHistoryStyle();
                    hist.WriteTo(tw);

                    Style buoy = this.MakeBuoyStyle();
                    buoy.WriteTo(tw);
                    #endregion

                    #region Storms
                    tw.WriteStartElement("Folder");
                    tw.WriteElementString("name", "Current Storms");

                    //Clear Previous Images
                    if (Directory.Exists(this.GetExecutingLocation() + "\\buoyImages"))
                        Directory.Delete(this.GetExecutingLocation() + "\\buoyImages", true);

                    lock (this._distanceLock)
                    {
                        foreach (Storm s in currStorms)
                        {
                            Dictionary<string, Buoy> bForStorm = this.GetBuoysForStorm(allBuoys, s);
                            s.Buoys = bForStorm;
                            //this.AddBuoysToUpload(bForStorm);

                            List<Radar> rForStorm = this.GetRadarsForStorm(allRadars, s);
                            s.Radars = rForStorm;

                            s.BuoyImgHref = this.HttpBox.Text;
                            s.WriteTo(tw);

                            s.Buoys = null;
                            s.Radars = null;
                        }
                    }
                    tw.WriteEndElement();//Model Forecasts
                    #endregion

                    #region SST Overlays
                    tw.WriteStartElement("Folder");
                    tw.WriteElementString("name", "SST");
                    KmlWriter.WriteListStyle(ListItemType.radioFolder, tw);
                    GroundOverlay sstOverlay = null;

                    #region SST Contoured
                    tw.WriteStartElement("Folder");
                    tw.WriteElementString("name", "SST Contoured");
                    tw.WriteElementString("visibility", "0");
                    #region Legend
                    ScreenOverlay sstContLeg = new ScreenOverlay("SST Contoured Legend", "", false, 
                        this.HttpBox.Text + "/sst_legend.png", 
                        new ScreenXY(ScreenUnits.fraction, 0, 1), 
                        new OverlayXY(ScreenUnits.fraction, 0, 1), 
                        new KmlUtils.Size(ScreenUnits.fraction, 0, 0), 
                        RefreshMode.once, 0);
                    sstContLeg.WriteTo(tw);
                    #endregion
                    lock (this._sstNames)
                    {
                        foreach (OverlayKey ok in this._sstNames.Values)
                        {
                            if (ok.ImgType == "contoured")
                            {
                                sstOverlay = new GroundOverlay(new KmlUtils.Icon(this.HttpBox.Text + "/" + ok.FileName, RefreshMode.onInterval, 3600, 0, 0, 0, 0),
                                    ok, null, ok.KmlName, null, 0, false);
                                sstOverlay.WriteTo(tw);
                            }
                        }
                    }
                    tw.WriteEndElement();//SST Contoured
                    #endregion

                    #region SST Smooth
                    tw.WriteStartElement("Folder");
                    tw.WriteElementString("name", "SST Smooth");
                    tw.WriteElementString("visibility", "0");
                    #region Legend
                    ScreenOverlay sstSmoothLeg = new ScreenOverlay("SST Smooth Legend", "", false,
                        this.HttpBox.Text + "/sst_legend.png",
                        new ScreenXY(ScreenUnits.fraction, 0, 1),
                        new OverlayXY(ScreenUnits.fraction, 0, 1),
                        new KmlUtils.Size(ScreenUnits.fraction, 0, 0),
                        RefreshMode.once, 0);
                    sstSmoothLeg.WriteTo(tw);
                    #endregion
                    lock (this._sstNames)
                    {
                        foreach (OverlayKey ok in this._sstNames.Values)
                        {
                            if (ok.ImgType == "smooth")
                            {
                                sstOverlay = new GroundOverlay(new KmlUtils.Icon(this.HttpBox.Text + "/" + ok.FileName, RefreshMode.onInterval, 3600, 0, 0, 0, 0),
                                    ok, null, ok.KmlName, null, 0, false);
                                sstOverlay.WriteTo(tw);
                            }
                        }
                    }
                    tw.WriteEndElement();//SST Smooth
                    #endregion

                    #region SST 26+
                    tw.WriteStartElement("Folder");
                    tw.WriteElementString("name", "SST 26+");
                    tw.WriteElementString("visibility", "0");
                    #region Legend
                    ScreenOverlay sst26Leg = new ScreenOverlay("SST 26+ Legend", "", false,
                        this.HttpBox.Text + "/sst_legend26+.png",
                        new ScreenXY(ScreenUnits.fraction, 0, 1),
                        new OverlayXY(ScreenUnits.fraction, 0, 1),
                        new KmlUtils.Size(ScreenUnits.fraction, 0, 0),
                        RefreshMode.once, 0);
                    sst26Leg.WriteTo(tw);
                    #endregion
                    lock (this._sstNames)
                    {
                        foreach (OverlayKey ok in this._sstNames.Values)
                        {
                            if (ok.ImgType == "twentySixPlus")
                            {
                                sstOverlay = new GroundOverlay(new KmlUtils.Icon(this.HttpBox.Text + "/" + ok.FileName, RefreshMode.onInterval, 3600, 0, 0, 0, 0),
                                    ok, null, ok.KmlName, null, 0, false);
                                sstOverlay.WriteTo(tw);
                            }
                        }
                    }
                    tw.WriteEndElement();//SST 26+
                    #endregion

                    #region SST 7 day Delta
                    tw.WriteStartElement("Folder");
                    tw.WriteElementString("name", "SST 7day Delta");
                    tw.WriteElementString("visibility", "0");
                    #region Legend
                    ScreenOverlay sst7Leg = new ScreenOverlay("SST 7day Delta Legend", "", false,
                        this.HttpBox.Text + "/sst_legend7day.png",
                        new ScreenXY(ScreenUnits.fraction, 0, 1),
                        new OverlayXY(ScreenUnits.fraction, 0, 1),
                        new KmlUtils.Size(ScreenUnits.fraction, 0, 0),
                        RefreshMode.once, 0);
                    sst7Leg.WriteTo(tw);
                    #endregion
                    lock (this._sstNames)
                    {
                        foreach (OverlayKey ok in this._sstNames.Values)
                        {
                            if (ok.ImgType == "7Delta")
                            {
                                sstOverlay = new GroundOverlay(new KmlUtils.Icon(this.HttpBox.Text + "/" + ok.FileName, RefreshMode.onInterval, 3600, 0, 0, 0, 0),
                                    ok, null, ok.KmlName, null, 0, false);
                                sstOverlay.WriteTo(tw);
                            }
                        }
                    }
                    tw.WriteEndElement();//SST 7 day
                    #endregion

                    #region SST 3 day Delta
                    tw.WriteStartElement("Folder");
                    tw.WriteElementString("name", "SST 3day Delta");
                    tw.WriteElementString("visibility", "0");
                    #region Legend
                    ScreenOverlay sst3Leg = new ScreenOverlay("SST 3day Delta Legend", "", false,
                        this.HttpBox.Text + "/sst_legend7day.png",
                        new ScreenXY(ScreenUnits.fraction, 0, 1),
                        new OverlayXY(ScreenUnits.fraction, 0, 1),
                        new KmlUtils.Size(ScreenUnits.fraction, 0, 0),
                        RefreshMode.once, 0);
                    sst3Leg.WriteTo(tw);
                    #endregion
                    lock (this._sstNames)
                    {
                        foreach (OverlayKey ok in this._sstNames.Values)
                        {
                            if (ok.ImgType == "3Delta")
                            {
                                sstOverlay = new GroundOverlay(new KmlUtils.Icon(this.HttpBox.Text + "/" + ok.FileName, RefreshMode.onInterval, 3600, 0, 0, 0, 0),
                                    ok, null, ok.KmlName, null, 0, false);
                                sstOverlay.WriteTo(tw);
                            }
                        }
                    }
                    tw.WriteEndElement();//SST 3 day
                    #endregion

                    #region None
                    KmlWriter.WriteEmpyFolderEntry("SST Off", true, tw);
                    //tw.WriteStartElement("Folder");
                    //tw.WriteElementString("name", "SST Off");
                    //tw.WriteElementString("open", "1");
                    ////tw.WriteElementString("visibility", "1");
                    //tw.WriteEndElement();// no SST
                    #endregion

                    tw.WriteEndElement();//SST
                    #endregion

                    #region Satellites
                    tw.WriteStartElement("Folder");
                    tw.WriteElementString("name", "Satellite Images");
                    tw.WriteElementString("visibility", "0");
                    KmlWriter.WriteListStyle(ListItemType.radioFolder, tw);

                    GroundOverlay wv = new GroundOverlay(new KmlUtils.Icon("http://cimss.ssec.wisc.edu/tropic/real-time/atlantic/images/xxwvg8.GIF", RefreshMode.onInterval, 300, 0, 0, 0, 0),
                        new LatLonBox(54.69196244693389, -4.703781499398805, -28.98222732555026, -100.8257883920368, 0), 
                        null, "Tropical Atlantic WV", null, 0, false);
                    wv.WriteTo(tw);

                    GroundOverlay ir = new GroundOverlay(new KmlUtils.Icon("http://cimss.ssec.wisc.edu/tropic/real-time/atlantic/images/xxirg8.GIF", RefreshMode.onInterval, 300, 0, 0, 0, 0),
                        new LatLonBox(54.69196244693389, -4.703781499398805, -28.98222732555026, -100.8257883920368, 0), 
                        null, "Tropical Atlantic IR", null, 0, false);
                    ir.WriteTo(tw);

                    GroundOverlay eir = new GroundOverlay(new KmlUtils.Icon("http://cimss.ssec.wisc.edu/tropic/real-time/atlantic/images/xxirg8n.GIF", RefreshMode.onInterval, 300, 0, 0, 0, 0),
                        new LatLonBox(54.69196244693389, -4.703781499398805, -28.98222732555026, -100.8257883920368, 0), 
                        null, "Tropical Atlantic EIR", null, 0, false);
                    eir.WriteTo(tw);

                    #region None
                    KmlWriter.WriteEmpyFolderEntry("Satellites Off", true, tw);
                    //tw.WriteStartElement("Folder");
                    //tw.WriteElementString("name", "Satellites Off");
                    //tw.WriteElementString("open", "0");
                    ////tw.WriteElementString("visibility", "1");
                    //tw.WriteEndElement();// no radars
                    #endregion

                    tw.WriteEndElement();//Satellites
                    #endregion

                    #region Analysis
                    tw.WriteStartElement("Folder");
                    tw.WriteElementString("name", "Analysis");
                    tw.WriteElementString("visibility", "0");
                    KmlWriter.WriteListStyle(ListItemType.radioFolder, tw);
                    
                    GroundOverlay winds = new GroundOverlay(new KmlUtils.Icon("http://cimss.ssec.wisc.edu/tropic/real-time/atlantic/winds/wg8wvir.GIF", RefreshMode.onInterval, 300, 0, 0, 0, 0),
                       new LatLonBox(48.083438, -3.860553, -29.763672, -100.522440, 0), 
                       null, "Mid Upper WV & IR Winds", null, 0, false);
                    winds.WriteTo(tw);

                    GroundOverlay shear = new GroundOverlay(new KmlUtils.Icon("http://cimss.ssec.wisc.edu/tropic/real-time/atlantic/winds/wg8shr.GIF", RefreshMode.onInterval, 300, 0, 0, 0, 0),
                       new LatLonBox(48.083438, -3.860553, -29.763672, -100.522440, 0), 
                       null, "Wind Shear Analysis", null, 0, false);
                    shear.WriteTo(tw);

                    GroundOverlay div = new GroundOverlay(new KmlUtils.Icon("http://cimss.ssec.wisc.edu/tropic/real-time/atlantic/winds/wg8dvg.GIF", RefreshMode.onInterval, 300, 0, 0, 0, 0),
                       new LatLonBox(48.083438, -3.860553, -29.763672, -100.522440, 0), 
                       null, "Upper Divergence", null, 0, false);
                    div.WriteTo(tw);

                    GroundOverlay con = new GroundOverlay(new KmlUtils.Icon("http://cimss.ssec.wisc.edu/tropic/real-time/atlantic/winds/wg8conv.GIF", RefreshMode.onInterval, 300, 0, 0, 0, 0),
                       new LatLonBox(48.083438, -3.860553, -29.763672, -100.522440, 0), 
                       null, "Lower Convergence", null, 0, false);
                    con.WriteTo(tw);

                    GroundOverlay vor = new GroundOverlay(new KmlUtils.Icon("http://cimss.ssec.wisc.edu/tropic/real-time/atlantic/winds/wg8vor4.GIF", RefreshMode.onInterval, 300, 0, 0, 0, 0),
                       new LatLonBox(59.782303, -1.502016, -13.444875, -119.844947, 0), 
                       null, "850 mb Vorticity", null, 0, false);
                    vor.WriteTo(tw);

                    GroundOverlay steer = new GroundOverlay(new KmlUtils.Icon("http://cimss.ssec.wisc.edu/tropic/real-time/atlantic/winds/wg8dlm5.GIF", RefreshMode.onInterval, 300, 0, 0, 0, 0),
                       new LatLonBox(59.782303, -1.502016, -13.444875, -119.844947, 0), 
                       null, "Mean Steering Layer", null, 0, false);
                    steer.WriteTo(tw);

                    #region None
                    KmlWriter.WriteEmpyFolderEntry("Analysis Off", true, tw);
                    //tw.WriteStartElement("Folder");
                    //tw.WriteElementString("name", "Analysis Off");
                    //tw.WriteElementString("open", "0");
                    ////tw.WriteElementString("visibility", "1");
                    //tw.WriteEndElement();// no radars
                    #endregion

                    tw.WriteEndElement();//Analysis
                    #endregion
                    
                    tw.WriteEndElement();//Document
                    tw.WriteEndElement();//kml
                    tw.Flush();
                    tw.Close();
                    fs.Close();
                    #endregion

                    #region ZipKml (Make Kmz)
                    AppendLog(string.Format("Kml: Zipping Kmz... "));
                    ZipOutputStream zo = new ZipOutputStream(new java.io.FileOutputStream("tropModels.kmz"));

                    // Add Buoy Images
                    AppendLog(string.Format("Adding Buoy Graphs to kmz file..."));
                    this.AddFolderToKmz(this.GetExecutingLocation() + "\\buoyImages", zo);

                    java.io.FileInputStream fis = new java.io.FileInputStream("tropModels.kml");
                    ZipEntry ze = new ZipEntry("tropModels.kml");
                    ze.setMethod(ZipEntry.DEFLATED);
                    zo.putNextEntry(ze);
                    sbyte[] buffer = new sbyte[1024];
                    int len = 0;

                    // Read and write until done 
                    while ((len = fis.read(buffer)) >= 0)
                    {
                        zo.write(buffer, 0, len);
                    }

                    zo.closeEntry();
                    fis.close();
                    zo.close();
                    #endregion

                    #region Push to Server
                    if (this.checkBox1.Checked && FTPLoginIsValid())
                    {
                        AppendLog(string.Format("Pushing Kmz to Ftp Server: {0} ...", this.FtpLoc.Text));
                        FTPClient fp = new FTPClient();
                        fp.Timeout = 45000;
                        fp.RemoteHost = this.GetFTPHostAndPath()[0];// this.FtpLoc.Text.Substring(0, this.FtpLoc.Text.IndexOf("/"));
                        fp.Connect();
                        fp.Login(this.FtpUser.Text, this.FtpPass.Text);
                        if (this.GetFTPHostAndPath().Length > 1 && this.GetFTPHostAndPath()[1] != null)
                            fp.ChDir(GetFTPHostAndPath()[1]);
                        fp.ConnectMode = FTPConnectMode.ACTIVE;
                        fp.TransferType = FTPTransferType.BINARY;

                        fp.Put("tropModels.kmz", "tropModels.kmz");

                        if (_newSST)
                        {
                            AppendLog("Pushing " + this._sstNames.Count + " SST images to server.");
                            lock (this._sstNames)
                            {
                                foreach (OverlayKey ok in this._sstNames.Values)
                                {
                                    fp.Put(ok.FileName, ok.FileName);
                                }
                            }
                            _newSST = false;
                        }
                        
                        //AppendLog(string.Format("Pushing Buoy Images to Ftp Server..."));
                        //foreach (Buoy b in this._uploadBuoys)
                        //{
                        //    string buoyFile = b.Id + ".png";
                        //    fp.Put(buoyFile, buoyFile);
                        //}

                        fp.Quit();
                        AppendLog(string.Format("Pushing to Ftp Server: {0} ...Done!", this.FtpLoc.Text));
                    }
                    #endregion
                }
                catch (FTPException ftpe)
                {
                    AppendLog("UploadKml: FTPException :" + Thread.CurrentThread.Name + "\n" + ftpe.Message);
                }
                catch (Exception e)
                {
                    AppendLog("UploadKml: " + e.Source + " " + Thread.CurrentThread.Name + "\n" + e.Message + "\n" + e.StackTrace);
                }

                try
                {
                    Thread.Sleep(this._kmlUploadInterval * 60 * 1000);
                }
                catch (ThreadInterruptedException)
                {
                    return;
                }
            }
        }

        private string[] GetFTPHostAndPath()
        {
            string[] ret = null;

            string ftp = this.FtpLoc.Text;
            string host = "";
            string path = "";

            int index = ftp.IndexOf("/");
            if (index > -1)
            {
                host = ftp.Substring(0, index);
                path = ftp.Substring(index);

                ret = new string[] { host, path };
            }
            else
                ret = new string[] { ftp };

            return ret;    
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (this.button1.Text.Equals("Start"))
            {
                this.checkBox1.Enabled = false;
                this.EnableFtpBoxes(false);

                _uploadThread = new Thread(new ThreadStart(this.UploadKml));
                _uploadThread.IsBackground = true;
                _uploadThread.Name = "KmlUploadThread";
                _uploadThread.Start();
                _sg.Start();
                _mg.Start();
                _ng.Start();
                this.button1.Text = "Stop";
            }
            else
            {
                this.checkBox1.Enabled = true;
                this.EnableFtpBoxes(true);
                _uploadThread.Interrupt();
                _sg.Stop();
                _mg.Stop();
                _ng.Stop();
                this.button1.Text = "Start";
            }
        }

        private bool ImagesReady()
        {
            bool ret = true;

            lock (this._sstNames)
            {
                if (this._sstNames.Count != 40)
                    ret = false;
            }

            if (!ret)
                AppendLog("SST Images Not Ready");

            return ret;
        }

        private string GetExecutingLocation()
        {
            string assbly = System.Reflection.Assembly.GetExecutingAssembly().Location;
            string loc = assbly.Substring(0, assbly.LastIndexOf("\\"));
            return loc;
        }

        private Dictionary<string, Buoy> GetAllBuoys()
        {
            Dictionary<string, Buoy> ret = new Dictionary<string, Buoy>();
            string dataFile = "";

            StreamReader sr = null;
            try
            {
                string execLoc = this.GetExecutingLocation();
                dataFile = execLoc + "\\Data\\buoys.txt";
                sr = new StreamReader(dataFile);
            }
            catch (Exception e)
            {
                AppendLog(e.Message + dataFile);
                return ret;
            }

            string line = "";
            while ((line = sr.ReadLine()) != null)
            {
                if (line.IndexOf("##") == 0 || line.Length == 0)
                    continue;
                else
                {
                    string[] toks = line.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
                    string id = toks[0];
                    KmlUtils.Point loc = new KmlUtils.Point(AltitudeMode.clampToGround, double.Parse(toks[1]), double.Parse(toks[2]), 0);
                    string infoUrl = toks[3];
                    string imgUrl = toks[4];
                    Buoy b = new Buoy(id, loc, infoUrl, imgUrl);
                    if (!ret.ContainsKey(b.Id))
                        ret.Add(b.Id, b);
                    else
                        AppendLog("Duplicate Buoy #" + b.Id + " in " + dataFile + "!");
                }
            }

            return ret;
        }

        private List<Radar> GetAllRadars()
        {
            List<Radar> ret = new List<Radar>();
            string dataFile = "";

            StreamReader sr = null;
            try
            {
                string execLoc = this.GetExecutingLocation();
                dataFile = execLoc + "\\Data\\radars.txt";
                sr = new StreamReader(dataFile);
            }
            catch (Exception e)
            {
                AppendLog(e.Message + dataFile);
                return ret;
            }

            string line = "";
            while ((line = sr.ReadLine()) != null)
            {
                if (line.IndexOf("##") == 0 || line.Length == 0)
                    continue;
                else
                {
                    Radar r = new Radar(line);
                    ret.Add(r);
                }
            }

            return ret;
        }

        private Dictionary<string, Buoy> GetBuoysForStorm(Dictionary<string, Buoy> allBuoys, Storm s)
        {
            // Make folder for buoy images if neccesary...we'll add these to the kmz as workaround for ballon images not updating
            if (!Directory.Exists(this.GetExecutingLocation() + "\\buoyImages"))
                Directory.CreateDirectory(this.GetExecutingLocation() + "\\buoyImages");
            string saveLoc = this.GetExecutingLocation() + "\\buoyImages";

            #region First Find Buoys that are close enough
            Dictionary<string, Buoy> currBuoys = new Dictionary<string, Buoy>();
            foreach (Buoy b in allBuoys.Values)
            {
                // Check range
                if (s.Initial.Location.GreatCircleDistance(b.Location).NM < this._maxBuoyDistFromStorm)
                    currBuoys.Add(b.Id, b);
            }
            #endregion
            #region Now Attempt to retrieve and Write the Graphs
            Dictionary<string, Buoy> ret = new Dictionary<string, Buoy>();
            foreach (Buoy b in currBuoys.Values)
            {
                Bitmap bm = null;
                Stream str = null;
                try
                {
                    HttpWebRequest req = HttpWebRequest.Create(b.ImgUrl) as HttpWebRequest;
                    req.Timeout = 15000;
                    HttpWebResponse res = req.GetResponse() as HttpWebResponse;
                    if (res.StatusCode == HttpStatusCode.OK)
                    {
                        string imgID = Guid.NewGuid().ToString();
                        b.LocalFileName = imgID + ".png";
                        System.Drawing.Imaging.ImageFormat fmt = System.Drawing.Imaging.ImageFormat.Jpeg;
                        str = res.GetResponseStream();
                        bm = Bitmap.FromStream(str) as Bitmap;
                        bm = ScaleImage(bm, 460, 345) as Bitmap;
                        bm.Save(saveLoc + "\\" + b.LocalFileName, fmt);
                        ret.Add(b.Id, b);
                    }
                    else if (res.StatusCode == HttpStatusCode.NotModified)
                    {
                        AppendLog("GetBuoyImage: Server responded " + res.StatusCode.ToString() + " for Buoy#" + b.Id);
                        ret.Add(b.Id, b);
                    }
                    else
                    {
                        AppendLog("GetBuoyImage: Server responded " + res.StatusCode.ToString() + " for Buoy#" + b.Id);
                        //Console.WriteLine("Bitmap Error");
                    }
                }
                catch (Exception e)
                {
                    AppendLog("GetBuoyImage: Exception was " + e.Message + " for Buoy#" + b.Id);
                    if (str != null)
                        str.Close();
                    if (bm != null)
                        bm.Dispose();
                }
            }
            #endregion
            return ret;
        }

        private List<Radar> GetRadarsForStorm(List<Radar> allRadars, Storm s)
        {
            #region Find Radars that are close enough
            List<Radar> ret = new List<Radar>();
            foreach (Radar r in allRadars)
            {
                // Check range
                if (s.Initial.Location.GreatCircleDistance(r.CenterLoc).NM < this._maxRadarDistFromStorm)
                    ret.Add(r);
            }
            #endregion

            return ret;
        }

        private void AddBuoysToUpload(List<Buoy> buoys)
        {
            foreach (Buoy b in buoys)
            {
                bool needsAdd = true;
                foreach (Buoy bb in this._uploadBuoys)
                {
                    if (bb.Id == b.Id)
                    {
                        needsAdd = false;
                    }
                }
                if (needsAdd)
                    this._uploadBuoys.Add(b);
            }
        }

        private Image ScaleImage(Image source, int MaxWidth, int MaxHeight)
        {

            float MaxRatio = MaxWidth / (float)MaxHeight;
            float ImgRatio = source.Width / (float)source.Height;

            if (source.Width > MaxWidth)
                return new Bitmap(source, new System.Drawing.Size(MaxWidth, (int)Math.Round(MaxWidth /
                ImgRatio, 0)));

            if (source.Height > MaxHeight)
                return new Bitmap(source, new System.Drawing.Size((int)Math.Round(MaxWidth * ImgRatio,
                0), MaxHeight));

            return source;

        }

        private bool LoadConfig()
        {
            FileStream stream = null;
            try
            {
                string fileName = AppDomain.CurrentDomain.BaseDirectory + "\\TropicalWatchConfig.xml";
                stream = new FileStream(fileName, FileMode.Open);

                XmlDocument doc = new XmlDocument();
                doc.Load(stream);

                XmlNode config = doc.SelectSingleNode("TropicalWatchConfig");
                string ftpLoc = config.SelectSingleNode("FTPServer").InnerText;
                string ftpUser = config.SelectSingleNode("FTPUser").InnerText;
                string ftpPass = config.SelectSingleNode("FTPPass").InnerText;
                string httpRoot = config.SelectSingleNode("HTTPRoot").InnerText;
                int radar = int.Parse(config.SelectSingleNode("RadarDistance").InnerText);
                int buoy = int.Parse(config.SelectSingleNode("BuoyDistance").InnerText);

                this.FtpLoc.Text = ftpLoc;
                this.FtpUser.Text = ftpUser;
                this.FtpPass.Text = ftpPass;
                this.HttpBox.Text = httpRoot;
                this.radarDist.Value = radar;
                this.buoyDist.Value = buoy;
                return true;
            }
            catch (Exception)
            {

            }
            finally
            {
                if (stream != null)
                    stream.Close();

            }
            return false;
        }

        #region Make Named Styles for our Document
        private Style MakeNHCStyle()
        {
            LineStyle nhcLine = new LineStyle("555555FF", ColorMode.normal, 3);
            IconStyle nhcIcon = new IconStyle(null, ColorMode.normal, .5, new KmlUtils.Icon(this.HttpBox.Text + "/nhc.png", RefreshMode.once, 0, 0, 0, 0, 0));
            LabelStyle nhcLabel = new LabelStyle("ff0000ff", ColorMode.normal, .5);
            List<ASubStyle> nhcStyles = new List<ASubStyle>();
            nhcStyles.Add(nhcLine);
            nhcStyles.Add(nhcIcon);
            nhcStyles.Add(nhcLabel);

            Style bamm = new Style("NHC", nhcStyles);
            return bamm;
        }

        private Style MakeBAMMStyle()
        {
            LineStyle bammLine = new LineStyle("ff0000ff", ColorMode.normal, 2);
            IconStyle bammIcon = new IconStyle(null, ColorMode.normal, .5, new KmlUtils.Icon(this.HttpBox.Text + "/red.png", RefreshMode.once, 0, 0, 0, 0, 0));
            LabelStyle bammLabel = new LabelStyle("ff0000ff", ColorMode.normal, .5);
            List<ASubStyle> bammStyles = new List<ASubStyle>();
            bammStyles.Add(bammLine);
            bammStyles.Add(bammIcon);
            bammStyles.Add(bammLabel);

            Style bamm = new Style("BAMM", bammStyles);
            return bamm;
        }

        private Style MakeBAMDStyle()
        {
            LineStyle bamdLine = new LineStyle("00ff00ff", ColorMode.normal, 2);
            IconStyle bamdIcon = new IconStyle(null, ColorMode.normal, .5, new KmlUtils.Icon(this.HttpBox.Text + "/green.png", RefreshMode.once, 0, 0, 0, 0, 0));
            LabelStyle bamdLabel = new LabelStyle("00ff00ff", ColorMode.normal, .5);
            List<ASubStyle> bamdStyles = new List<ASubStyle>();
            bamdStyles.Add(bamdLine);
            bamdStyles.Add(bamdIcon);
            bamdStyles.Add(bamdLabel);

            Style bamd = new Style("BAMD", bamdStyles);
            return bamd;
        }

        private Style MakeA98EStyle()
        {
            LineStyle a98eLine = new LineStyle("0000ffff", ColorMode.normal, 2);
            IconStyle a98eIcon = new IconStyle(null, ColorMode.normal, .5, new KmlUtils.Icon(this.HttpBox.Text + "/blue.png", RefreshMode.once, 0, 0, 0, 0, 0));
            LabelStyle a98eLabel = new LabelStyle("0000ffff", ColorMode.normal, .5);
            List<ASubStyle> a98eStyles = new List<ASubStyle>();
            a98eStyles.Add(a98eLine);
            a98eStyles.Add(a98eIcon);
            a98eStyles.Add(a98eLabel);

            Style a98e = new Style("A98E", a98eStyles);
            return a98e;
        }

        private Style MakeLBARStyle()
        {
            LineStyle lbarLine = new LineStyle("ffff00ff", ColorMode.normal, 2);
            IconStyle lbarIcon = new IconStyle(null, ColorMode.normal, .5, new KmlUtils.Icon(this.HttpBox.Text + "/yellow.png", RefreshMode.once, 0, 0, 0, 0, 0));
            LabelStyle lbarLabel = new LabelStyle("ffff00ff", ColorMode.normal, .5);
            List<ASubStyle> lbarStyles = new List<ASubStyle>();
            lbarStyles.Add(lbarLine);
            lbarStyles.Add(lbarIcon);
            lbarStyles.Add(lbarLabel);

            Style lbar = new Style("LBAR", lbarStyles);
            return lbar;
        }

        private Style MakeInitialStyle()
        {
            IconStyle initialIcon = new IconStyle(null, ColorMode.normal, .5, new KmlUtils.Icon(this.HttpBox.Text + "/white.png", RefreshMode.once, 0, 0, 0, 0, 0));
            List<ASubStyle> initialStyles = new List<ASubStyle>();
            initialStyles.Add(initialIcon);

            Style initial = new Style("Initial", initialStyles);
            return initial;
        }

        private Style MakeTrackHistoryStyle()
        {
            IconStyle histIcon = new IconStyle(null, ColorMode.normal, .5, new KmlUtils.Icon(this.HttpBox.Text + "/x.png", RefreshMode.once, 0, 0, 0, 0, 0));
            LineStyle histLine = new LineStyle("AAAAAAff", ColorMode.normal, 2);
            List<ASubStyle> histStyles = new List<ASubStyle>();
            histStyles.Add(histIcon);
            histStyles.Add(histLine);

            Style initial = new Style("Hist", histStyles);
            return initial;
        }

        private Style MakeBuoyStyle()
        {
            IconStyle buoyIcon = new IconStyle("ff0000ff", ColorMode.normal, .6, new KmlUtils.Icon("root://icons/palette-4.png", RefreshMode.once, 0, 0, 128, 32, 32));
            LabelStyle buoyLabel = new LabelStyle("ff0000ff", ColorMode.normal, .6);
            List<ASubStyle> buoyStyles = new List<ASubStyle>();
            buoyStyles.Add(buoyIcon);
            buoyStyles.Add(buoyLabel);

            Style buoy = new Style("Buoy", buoyStyles);
            return buoy;
        }
        #endregion

        #region ZipHelpers
        private void AddFolderToZip(String path, String srcFolder, ZipOutputStream zip)
        {
            java.io.File folder = new java.io.File(srcFolder);
            String[] fileListe = folder.list();
            try
            {
                int i = 0;
                while (i < fileListe.GetLength(0))
                {
                    //string test = path + "/" + folder.getName();
                    string test = folder.getName();
                    string test2 = srcFolder + "/" + fileListe[i];
                    AddToZip(test, test2, zip);
                    i++;
                }
            }
            catch (Exception ex)
            {
            }
        }

        private void AddToZip(String path, String srcFile, ZipOutputStream zip)
        {

            java.io.File folder = new java.io.File(srcFile);
            if (folder.isDirectory())
            {
                AddFolderToZip(path, srcFile, zip);
            }
            else
            {
                //			 Transfer bytes from in to out
                sbyte[] buf = new sbyte[1024];
                int len;
                try
                {
                    java.io.FileInputStream fi = new java.io.FileInputStream(srcFile);
                    zip.putNextEntry(new ZipEntry(path + "/" + folder.getName()));
                    while ((len = fi.read(buf)) >= 0)
                    {
                        zip.write(buf, 0, len);
                    }
                }
                catch (Exception ex)
                {
                    //ex.printStackTrace();
                }
            }
        }

        private void AddFolderToKmz(String srcFolder, ZipOutputStream zip)
        {
            AddFolderToZip("", srcFolder, zip);
            try
            {
                zip.flush();                
            }
            catch (Exception ex)
            {
                //ex.printStackTrace();
            }
        }
        #endregion

        #region UI Methods
        private void buoyDist_ValueChanged(object sender, EventArgs e)
        {
            lock (_distanceLock)
            {
                this._maxBuoyDistFromStorm = (int)buoyDist.Value;
            }
        }

        private void radarDist_ValueChanged(object sender, EventArgs e)
        {
            lock (_distanceLock)
            {
                this._maxRadarDistFromStorm = (int)radarDist.Value;
            }
        }
        #endregion

        private void Form1_Load(object sender, EventArgs e)
        {
            if (this.LoadConfig())
                this.button1_Click(this, new EventArgs());
        }
    }
}